package com.cal.calonecal.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cal.calonecal.common.Response;
import com.cal.calonecal.mapper.*;
import com.cal.calonecal.pojo.dto.CreateExamDTO;
import com.cal.calonecal.pojo.dto.EquationExamDTO;
import com.cal.calonecal.pojo.dto.ExamDTO;
import com.cal.calonecal.pojo.dto.ExamSubmitDTO;
import com.cal.calonecal.pojo.entity.*;
import com.cal.calonecal.pojo.vo.EquationVO;
import com.cal.calonecal.pojo.vo.ExamVO;
import com.cal.calonecal.pojo.vo.JudgeResultVO;
import com.cal.calonecal.pojo.vo.UserProblemVO;
import com.cal.calonecal.service.IExamService;
import com.cal.calonecal.utils.EquationUtil;
import com.cal.calonecal.utils.ExamUtil;
import com.cal.calonecal.utils.UserUtil;
import org.nfunk.jep.JEP;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * @author 76004
 * @apiNote
 * @date 2023/5/11 14:46
 */
@Service
public class ExamServiceImpl extends ServiceImpl<ExamMapper, ExamEntity> implements IExamService {
    @Autowired
    private EquationUtil equationUtil;

    @Autowired
    private MappingUserExamMapper mappingUserExamMapper;

    @Autowired
    private EquationMapper equationMapper;

    @Autowired
    private MappingEquationExamMapper mappingEquationExamMapper;
    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private CompleteEquationMapper wrongEquationMapper;

    @Autowired
    private UserUtil userUtil;

    @Autowired
    private ExamUtil examUtil;


    @Resource(name = "exam-judge-pool")
    private Executor executor;
    @Override
    @Async("exam-judge-pool")
    public CompletableFuture<Response<JudgeResultVO>> judge(ExamSubmitDTO problemSubmitDTO) {

        JEP jep = new JEP();
        JudgeResultVO judgeResultVO = new JudgeResultVO();
        int correctNum = 0;
        List<EquationVO>equationExamVOS = new ArrayList<>();

        for(EquationExamDTO equationExamDTO: problemSubmitDTO.getEquations()){
            DecimalFormat df = new DecimalFormat("#.00");
            EquationVO equationExamVO = new EquationVO();
            double ans = 0;
            if(equationExamDTO.getType() == 1){
                //不是方程
                jep.parseExpression(equationExamDTO.getEquation());
                ans = jep.getValue();
                BeanUtil.copyProperties(equationExamDTO, equationExamVO);
                equationExamVO.setAns(ans);
            }else if(equationExamDTO.getType() == 2){
                //是方程,生成的方程都是a*x+b=c，先将abc分离出来
                String[] equation = equationExamDTO.getEquation().split("=");
                jep.parseExpression(equation[1]);
                double c = jep.getValue();
                String aStr = equation[0].substring(0,equation[0].indexOf("*"));
                //带符号b
                String bStr = equation[0].substring(equation[0].indexOf("x")+1,equation[0].length());
                jep.parseExpression(aStr);
                double a = jep.getValue();
                jep.parseExpression(bStr);
                double b = jep.getValue();
                System.out.println("a:"+a+",b:"+b+",c:"+c);
                ans = (c-b)/a;
                //初始化算式评测结果
                BeanUtil.copyProperties(equationExamDTO, equationExamVO);
                //格式化结果四舍五入保留两位小数
                equationExamVO.setAns(Double.parseDouble(df.format(ans)));
            }
            //初始化做题记录
            CompleteEquationEntity completeEquationEntity = BeanUtil.copyProperties(equationExamDTO, CompleteEquationEntity.class, "id");
            completeEquationEntity.setId(equationExamDTO.getId());
            System.out.println(completeEquationEntity.getId());
            completeEquationEntity.setUid(problemSubmitDTO.getUid());
            completeEquationEntity.setExamId(problemSubmitDTO.getId());
            completeEquationEntity.setEquationId(equationExamDTO.getId());
            completeEquationEntity.setCreateTime(new Date());
            if(equationExamDTO.getUAns() != null && Double.parseDouble(df.format(ans)) == Double.parseDouble(df.format(equationExamDTO.getUAns()))){
                correctNum++;
                if(completeEquationEntity.getCorrectCount() != null && completeEquationEntity.getCorrectCount() <= 3){
                    completeEquationEntity.setCorrectCount(completeEquationEntity.getCorrectCount()+1);
                }else{
                    completeEquationEntity.setCorrectCount(4);
                    completeEquationEntity.setIsCorrect(1);
                }
                equationExamVO.setIsCorrect(1);
            }else{
                completeEquationEntity.setIsCorrect(0);
                completeEquationEntity.setCorrectCount(0);
                equationExamVO.setIsCorrect(0);
            }
            equationExamVOS.add(equationExamVO);
            //添加用户做题记录
            if (completeEquationEntity.getId() == null) {
                wrongEquationMapper.insert(completeEquationEntity);
            }else{
                wrongEquationMapper.updateById(completeEquationEntity);
            }
        }
        judgeResultVO.setCorrectNum(correctNum);
        judgeResultVO.setEquationExamVOS(equationExamVOS);
        judgeResultVO.setUid(problemSubmitDTO.getUid());

        MappingUserExamEntity mappingUserExamEntity = null;
        // 将此次试卷结果保存
        if(problemSubmitDTO.getId() == 0){
            //错题
            mappingUserExamEntity = new MappingUserExamEntity();
        } else{
            mappingUserExamEntity =
                    mappingUserExamMapper.selectOne(new QueryWrapper<MappingUserExamEntity>().
                            eq("eid",problemSubmitDTO.getId()).eq("uid",problemSubmitDTO.getUid()));
            mappingUserExamEntity.setEndTime(examMapper.selectById(problemSubmitDTO.getId()).getEndTime());
        }

        System.out.println(problemSubmitDTO.getId());
        if(problemSubmitDTO.getId() == 0){
            //错题练习
            mappingUserExamEntity.setEid(0L);
        }else{
            mappingUserExamEntity.setEid(problemSubmitDTO.getId());
        }
        mappingUserExamEntity.setUid(problemSubmitDTO.getUid());
        mappingUserExamEntity.setCorrectNum(correctNum);

        mappingUserExamMapper.updateById(mappingUserExamEntity);
        return CompletableFuture.completedFuture(Response.success(judgeResultVO));
    }

    @Override
    @Transactional
    public Response<ExamVO> createExam(CreateExamDTO createExamDTO) {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        UserInfo userInfo = userUtil.getUser(request);
        if(userInfo.getType() != 1){
            throw new RuntimeException("越权操作");
        }
        Long eid = examUtil.getExamId();
        //创建算式
        List<EquationVO> equationVOS = equationUtil.createEquations(createExamDTO.getLevel(), createExamDTO.getNum(), eid);

        ExamVO examVO = new ExamVO();
        BeanUtil.copyProperties(createExamDTO, examVO);
        examVO.setEquations(equationVOS);
        ExamEntity examEntity = new ExamEntity();
        BeanUtil.copyProperties(createExamDTO, examEntity);
        //生成试卷模板id
        examEntity.setId(eid);
        examEntity.setCreateTime(new Date());
        examEntity.setEquationNum(createExamDTO.getNum());
        save(examEntity);
        return Response.success(examVO);
    }

    @Override
    public Response<List<UserProblemVO>> getUserProblem(HttpServletRequest httpServletRequest) {
        Long uid = Long.valueOf(httpServletRequest.getHeader("Authentication"));
        List<UserProblemVO> userProblemVOS = mappingUserExamMapper.selectUserProblem(uid);
        return Response.success(userProblemVOS);
    }

    @Override
    public Response<ExamVO> practiceAnotherExam(ExamDTO examDTO) {
        ExamEntity examEntity = getById(examDTO.getExamId());
        ExamVO examVO = new ExamVO();
        BeanUtil.copyProperties(examEntity,examVO);
        MappingUserExamEntity mappingUserExamEntity = new MappingUserExamEntity();
        mappingUserExamEntity.setEid(examDTO.getExamId());
        mappingUserExamEntity.setUid(examDTO.getUid());
        mappingUserExamEntity.setCreateTime(new Date());
        mappingUserExamMapper.insert(mappingUserExamEntity);
        //从映射表中查询出对应算式的id，然后查询对应的算式VO
        examVO.setEquations(mappingEquationExamMapper.selectList(
                new QueryWrapper<MappingEquationExam>().eq("exam_id",examDTO.getExamId())).stream().map(mappingEntity->{
                    EquationVO equationVO = new EquationVO();
                    BeanUtil.copyProperties(equationMapper.selectById(mappingEntity.getEquationId()),equationVO);
                    return equationVO;
        }).collect(Collectors.toList()));

        return Response.success(examVO);
    }

    @Override
    public Response<List<EquationVO>> getEquation(Long eid) {
        LambdaQueryWrapper<MappingEquationExam> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MappingEquationExam::getExamId, eid);
        List<MappingEquationExam> mappingEquationExams = mappingEquationExamMapper.selectList(wrapper);
        List<Long> equationIds = mappingEquationExams.stream().map(MappingEquationExam::getEquationId).collect(Collectors.toList());

        List<EquationEntity> equationEntities = equationMapper.selectBatchIds(equationIds);

        List<EquationVO> res = equationEntities.stream().map(temp ->
                {
                    EquationVO target = new EquationVO();
                    BeanUtils.copyProperties(temp, target);
                    return target;
                }

        ).collect(Collectors.toList());


        return Response.success(res);
    }

    @Override
    public void download(HttpServletResponse response, Integer examId) {
        List<EquationVO> rows = this.getEquation(new Long(examId)).getData();

        ExcelWriter writer = ExcelUtil.getWriter(true);

        writer.addHeaderAlias("id","题号");
        writer.addHeaderAlias("level","题目等级");
        writer.addHeaderAlias("equation","题干");
        writer.addHeaderAlias("ans","正确答案");
        writer.addHeaderAlias("uAns","您的答案");
        writer.setOnlyAlias(true);

        writer.merge(4, "试卷");

        writer.write(rows, true);

        //response为HttpServletResponse对象
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        //test.xls是弹出下载对话框的文件名，不能为中文，中文请自行编码

        response.setHeader("Content-Disposition","attachment;filename="+ "examPaper" + ".xlsx");

        ServletOutputStream out= null;
        try {
            out = response.getOutputStream();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        writer.flush(out, true);
        // 关闭writer，释放内存
        writer.close();
        //此处记得关闭输出Servlet流
        IoUtil.close(out);
    }

    @Override
    public Response<List<EquationVO>> startExam(Long eid, HttpServletRequest httpServletRequest) {
        UserInfo userInfo = userUtil.getUser(httpServletRequest);
        ExamEntity examEntity = getById(eid);
        if(examEntity == null){
            throw new RuntimeException("比赛不存在");
        }
        MappingUserExamEntity mappingUserExamEntity =
                mappingUserExamMapper.selectOne(new QueryWrapper<MappingUserExamEntity>().eq("uid", userInfo.getId())
                        .eq("eid", eid));
        if(mappingUserExamEntity == null){
            throw new RuntimeException("未报名该比赛");
        }
        if(examEntity.getStartTime().after(new Date()) || new Date().after(examEntity.getEndTime())){
            throw new RuntimeException("比赛不在规定时间");
        }
        if(mappingUserExamEntity.getCorrectNum() != null){
            throw new RuntimeException("您已经完成该场次了");
        }

        //获取题目VO
        List<EquationVO>equationVOS = mappingEquationExamMapper
                .selectList(new QueryWrapper<MappingEquationExam>().eq("exam_id",eid)).stream().map(mapping->{
                    return BeanUtil.copyProperties(equationMapper.selectById(mapping.getEquationId()), EquationVO.class);
                }).collect(Collectors.toList());

        return Response.success(equationVOS);
    }

    @Override
    public Response<Void> signUp(Long eid, HttpServletRequest httpServletRequest) {
        UserInfo userInfo = userUtil.getUser(httpServletRequest);
        ExamEntity examEntity = getById(eid);
        if(examEntity == null){
            throw new RuntimeException("比赛不存在");
        }
        if(null != mappingUserExamMapper.selectOne(new QueryWrapper<MappingUserExamEntity>().eq("uid",userInfo.getId()).eq("eid", eid))){
            throw new RuntimeException("已经报名了");
        }
        //设置报名信息
        MappingUserExamEntity mappingUserExamEntity = BeanUtil.copyProperties(examEntity, MappingUserExamEntity.class);
        mappingUserExamEntity.setUid(userInfo.getId());
        mappingUserExamEntity.setEid(eid);
        mappingUserExamMapper.insert(mappingUserExamEntity);
        return Response.success();
    }


    public Response<List<ExamVO>>getExamList(Integer pageNum, Integer pageSize, Long classId){
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        UserInfo userInfo = userUtil.getUser(request);
        if(classId == 0){
            return Response.success(page(new Page<ExamEntity>(pageNum, pageSize), new QueryWrapper<ExamEntity>()).getRecords().stream().map(entity->{
                ExamVO examVO = BeanUtil.copyProperties(entity, ExamVO.class);
                if(null == mappingUserExamMapper.selectOne(new QueryWrapper<MappingUserExamEntity>().eq("eid",entity.getId()).eq("uid", userInfo.getId()).orderByAsc())){
                    examVO.setIsSignup(0);
                }else {
                    examVO.setIsSignup(1);
                    //比赛未开始：1，否则-1
                    examVO.setIfStart(entity.getStartTime().compareTo(new Date()));
                }

                return examVO;
            }).collect(Collectors.toList()));
        }
        return Response.success(page(new Page<ExamEntity>(pageNum, pageSize), new QueryWrapper<ExamEntity>().eq("class_id", classId)).getRecords().stream().map(entity->{
            ExamVO examVO = BeanUtil.copyProperties(entity, ExamVO.class);
            if(null == mappingUserExamMapper.selectOne(new QueryWrapper<MappingUserExamEntity>().eq("eid",entity.getId()).eq("uid", userInfo.getId()).orderByAsc())){
                examVO.setIsSignup(0);
            }else {
                examVO.setIsSignup(1);
                //比赛未开始：1，否则-1
                examVO.setIfStart(entity.getStartTime().compareTo(new Date()));
            }

            return examVO;
        }).collect(Collectors.toList()));
    }


}
